using System;
using Firestarter.DB.Schema.BO;
using FireStarterModeller.BO;
using FireStarterModeller.Logic.Wizards;
using Habanero.BO;
using Habanero.Testability;
using NUnit.Framework;
using Rhino.Mocks;

namespace FireStarterModeller.Test.Logic
{
    [TestFixture]
    public class TestDMPropertyDeleter
    {
        #region Setup/Teardown

        [SetUp]
        public virtual void SetupTest()
        {
            TestUtilsLogic.SetupTest();
        }

        [TearDown]
        public virtual void TearDownTest()
        {
        }

        #endregion

        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            TestUtilsLogic.SetupFixture();
        }

        [Test]
        public void Test_Construct_WhenClassNull_ShouldRaiseError()
        {
            //---------------Set up test pack-------------------
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                new DMPropertyDeleter(null);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("dmProperty", ex.ParamName);
            }
        }

        [Test]
        public void Test_Construct_ShouldSetDMProperty()
        {
            //---------------Set up test pack-------------------
            var mockDMProperty = MockRepository.GenerateMock<DMProperty>(ConstructForFakes.True);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var dmPropertyDeleter = new DMPropertyDeleter(mockDMProperty);
            //---------------Test Result -----------------------
            Assert.AreSame(mockDMProperty, dmPropertyDeleter.DMProperty);
        }
        [Test]
        public void Test_Delete_WhenNoRelationshipPropsAndNoUCProps_ShouldMarkForDelete()
        {
            //If the Class is not involved in any Relationship and is not 
            // being used in Any Lookup Lists then it should still be deleted.
            //---------------Set up test pack-------------------
            var dmProperty = new DMProperty();
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsEmpty(dmProperty.RelationshipProperties);
            Assert.IsEmpty(dmProperty.DMUniqueConstraintProperties);
            Assert.IsNull(dmProperty.LookupList);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
        }

        /// <summary>
        /// If the Property is involved in any Relationship then it should be deleted along with its RelationshipProperty.
        /// </summary>
        [Test]
        public void Test_Delete_WhenHasRelationshipProp_ShouldMarkForDeleteAndMarkRelsForDelete()
        {
            //---------------Set up test pack-------------------
            IDMProperty dmProperty = new DMProperty();
            var relationshipProperty = new DMRelationshipProperty();
            dmProperty.RelationshipProperties.Add(relationshipProperty);
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsNotEmpty(dmProperty.RelationshipProperties);
            Assert.IsFalse(relationshipProperty.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsTrue(relationshipProperty.Status.IsDeleted);
        }
        /// <summary>
        /// If the Property is involved in any Relationship then it should be deleted along with its RelationshipProperty.
        /// </summary>
        [Test]
        public void Test_Delete_WhenHasRelationships_ShouldMarkRelatedPropForDelete()
        {
            //---------------Set up test pack-------------------
            IDMProperty dmProperty = new DMProperty();
            var relatedProperty = new DMProperty();
            var relationshipProperty = new DMRelationshipProperty {RelatedProperty = relatedProperty};
            dmProperty.RelationshipProperties.Add(relationshipProperty);
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsNotEmpty(dmProperty.RelationshipProperties);
            Assert.IsFalse(relationshipProperty.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            Assert.IsFalse(relatedProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsTrue(relationshipProperty.Status.IsDeleted);
            Assert.IsFalse(relatedProperty.Status.IsDeleted, "Should Not Mark RelatedProp for Delete");
        }
        /// <summary>
        /// If the Property is involved in any UniqueConstraint then it should be deleted along with its UniqueConstraintProperty.
        /// </summary>
        [Test]
        public void Test_Delete_WhenHasUC_ShouldMarkUCPropForDelete()
        {
            //---------------Set up test pack-------------------
            IDMProperty dmProperty = new DMProperty();
            var ucProp = new DMUniqueConstraintProperty ();
            dmProperty.DMUniqueConstraintProperties.Add(ucProp);
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsNotEmpty(dmProperty.DMUniqueConstraintProperties);
            Assert.IsFalse(ucProp.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsTrue(ucProp.Status.IsDeleted);
        }
        /// <summary>
        /// If the Property is specified using a Lookup list then it should be deleted along with its LookupList.
        /// </summary>
        [Test]
        public void Test_Delete_WhenLookupList_ShouldMarkLookupListForDelete()
        {
            //---------------Set up test pack-------------------
            var lookupList = new DMLookupList();
            IDMProperty dmProperty = new DMProperty {LookupList = lookupList};
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsFalse(lookupList.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsTrue(lookupList.Status.IsDeleted);
        }
        /// <summary>
        /// If the Property is specified using a DefaultPropRule then it should be deleted along with its DefaultPropRule.
        /// </summary>
        [Test]
        public void Test_Delete_WhenDefaultPropRule_ShouldMarkDefaultPropRuleForDelete()
        {
            //---------------Set up test pack-------------------
            var propRule = new DMPropRule();
            IDMProperty dmProperty = new DMProperty {DefaultPropRule = propRule};
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsFalse(propRule.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsTrue(propRule.Status.IsDeleted);
        }
        /// <summary>
        /// If the Property is specified on a grid, form or filter then it should be deleted along with its Property.
        /// </summary>
        //[Test]
        //public void Test_Delete_WhenHasUIProp_ShouldMarkUIPropForDelete()
        //{
        //    //---------------Set up test pack-------------------
        //    var uiProp = new UIProp();
        //    IDMProperty dmProperty = TestUtilsDMProperty.GetTestFactory()
        //        .WithValue(property => property.Class, null)
        //        .CreateValidBusinessObject();
        //    uiProp.PropertyName = dmProperty.PropertyName;
           
        //    var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
        //    //---------------Assert Precondition----------------
        //    Assert.IsNotNullOrEmpty(dmProperty.PropertyName);
        //    Assert.AreEqual(dmProperty.PropertyName, uiProp.PropertyName);
        //    Assert.IsFalse(uiProp.Status.IsDeleted);
        //    Assert.IsFalse(dmProperty.Status.IsDeleted);
        //    //---------------Execute Test ----------------------
        //    dmPropertyDeleter.Delete();
        //    //---------------Test Result -----------------------
        //    Assert.IsTrue(dmProperty.Status.IsDeleted);
        //    Assert.IsTrue(uiProp.Status.IsDeleted);
        //}

        /// <summary>
        /// If the Property is specified on a grid, form or filter then it should be deleted along with its Property.
        /// </summary>
        //[Test]
       // public void Test_Delete_WhenHasUIProp_HasSamePropNameButOnDifferntClass_ShouldNotMarkUIPropForDelete()
        //{
            //---------------Set up test pack-------------------
            /*var uiProp = new UIProp();
           IDMProperty dmProperty = TestUtilsDMProperty.GetTestFactory()
                .WithValue(property => property.Class, null)
                .CreateValidBusinessObject();
            uiProp.PropertyName = dmProperty.PropertyName;
           
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsNotNullOrEmpty(dmProperty.PropertyName);
            Assert.AreEqual(dmProperty.PropertyName, uiProp.PropertyName);
            Assert.IsFalse(uiProp.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
           Assert.IsTrue(uiProp.Status.IsDeleted);*/
           // Assert.Fail("NYI");
       // }
        /// <summary>
        /// If the Property has a MappedDBColumn it should not be deleted along with its MappedDBColumn.
        /// The reason for this is that the DBColumn should be present in the case that the 
        /// user reverse engineers from the DB again they probably do not want the DBColumn to create 
        /// the property that has just been deleted again.
        ///  
        /// This is dealt with via the fact that the relationship is set to DoNothing.
        /// </summary>
        [Test]
        public void Test_Delete_WhenMappedDBColumn_ShouldNotMarkMappedDBColumnForDelete()
        {
            //---------------Set up test pack-------------------
            var dbColumn = new DBColumn();
            IDMProperty dmProperty = new DMProperty { MappedDBColumn = dbColumn };
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsFalse(dbColumn.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmProperty.Status.IsDeleted);
            Assert.IsFalse(dbColumn.Status.IsDeleted, "The DBColumn should not be deleted");
        }

        /// <summary>
        /// If the Property is involved in any UIProp i.e. it is used in form, grid or filter then the DMProperty should be deleted 
        /// along with its UIProp.
        /// </summary>
        [Test]
        public void Test_Delete_WhenHasUIProps_ShouldMarkRelsForDelete()
        {
            //---------------Set up test pack-------------------
            DMProperty dmProperty = new DMProperty();
            var relatedUIProp = new UIProp();
            dmProperty.UIProps.Add(relatedUIProp);
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.IsNotEmpty(dmProperty.UIProps);
            Assert.IsFalse(relatedUIProp.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(relatedUIProp.Status.IsDeleted);
        }

        /// <summary>
        /// If the Property is involved in a relationship as the FK and it is the only Property definining that relationship then
        /// should delete the relationship.
        /// </summary>
        /// <returns></returns>
        [Test]
        public void Test_Delete_WhenHasRelProp_AndRelPropIsSimple_ShouldMarkForDeleteRelationship()
        {
            //---------------Set up test pack-------------------
            var dmClass = TestUtilsShared.CreateSavedDMClassWithRelationships();
            var dmRelationship = dmClass.RelationshipsDM[0];
            var dmRelationshipProperty = dmRelationship.RelationshipProperties[0];
            var dmProperty = dmRelationshipProperty.Property;
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dmProperty.RelationshipProperties.Count, "Should be a simple relationship");
            Assert.AreEqual(1, dmRelationship.RelationshipProperties.Count, "Should be a simple relationship");
            Assert.IsFalse(dmRelationship.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsTrue(dmRelationship.Status.IsDeleted);
        }
        /// <summary>
        /// If the Property is involved in a relationship as the FK and it is Part of a Complex FK definining that relationship
        ///  then should not delete the relationship.
        /// </summary>
        /// <returns></returns>
        [Test]
        public void Test_Delete_WhenHasRelProp_AndRelPropIsComplex_ShoulNotdMarkForDeleteRelationship()
        {
            //---------------Set up test pack-------------------
            var dmRelationship = TestUtilsShared.CreateSavedRelationshipInfoWithTwoRelatedProperties();
            var dmRelationshipProperty = dmRelationship.RelationshipProperties[0];
            var dmProperty = dmRelationshipProperty.Property;
            var dmPropertyDeleter = new DMPropertyDeleter(dmProperty);
            //---------------Assert Precondition----------------
            Assert.AreEqual(2, dmRelationship.RelationshipProperties.Count, "Should be a complex relationship");
            Assert.IsFalse(dmRelationship.Status.IsDeleted);
            Assert.IsFalse(dmProperty.Status.IsDeleted);
            //---------------Execute Test ----------------------
            dmPropertyDeleter.Delete();
            //---------------Test Result -----------------------
            Assert.IsFalse(dmRelationship.Status.IsDeleted, "Should not be deleted");
        }

        private static IDMProperty GetMockDMProperty()
        {
            var dmProperty = MockRepository.GenerateStub<DMProperty>(ConstructForFakes.True);
            dmProperty.Stub(prop => prop.RelationshipProperties).Return(new FakeBusinessObjectCollection<DMRelationshipProperty>());
            dmProperty.Stub(prop => prop.DMUniqueConstraintProperties).Return(new FakeBusinessObjectCollection<DMUniqueConstraintProperty>());
            return dmProperty;
        }
    }
}